//
//  BuildingOverview.m
//  3DModelCollector
//
//  Created by Jing Zhang on 7/16/12.
//  Copyright (c) 2012 Tencent. All rights reserved.
//

#import "BuildingOverview.h"

#define VERTEX_RADIUS   2

typedef struct BuildingBound_ {
    
    double  minLog;
    double  maxLog;
    double  minLat;
    double  maxLat;
}
BuildingBound;

@interface BuildingOverview()

@property (nonatomic) CGRect buildingRect;
@property (nonatomic) BuildingBound buildingBound;
@property (nonatomic, retain) NSArray* vertexArray;

@property (nonatomic, retain) CLLocationManager* locationManager;
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic) double heading;

@property (nonatomic, retain) UIImageView* compassView;
@property (nonatomic, retain) UIImageView* locationView;
@property (nonatomic, retain) UIImageView* photoLocationView;

- (void)drawBuilding;
- (void)drawCompass;
- (void)drawLocation;
- (void)drawPhotoLocation;

- (void)prepare;
- (void)onTapView:(UIGestureRecognizer*)gestureRecognizer;
- (int)sideIndexOnTap:(CGPoint)point;
- (CGFloat)distanceOfPoint1:(CGPoint)p1 point2:(CGPoint)p2;
- (CGPoint)locationToScreenPoint:(CLLocationCoordinate2D)coordinate;

@end

@implementation BuildingOverview

@synthesize building = _building;
@synthesize selectedSide = _selectedSide;
@synthesize locationEnabled = _locationEnabled;
@synthesize selectionEnabled = _selectionEnabled;
@synthesize delegate = _delegate;

@synthesize buildingRect = _buildingRect;
@synthesize buildingBound = _buildingBound;
@synthesize vertexArray = _vertexArray;

@synthesize locationManager = _locationManager;
@synthesize coordinate = _coordinate;
@synthesize heading = _heading;

@synthesize compassView = _compassView;
@synthesize locationView = _locationView;
@synthesize photoLocationView = _photoLocationView;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.userInteractionEnabled = YES;
        
        
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    
    self = [super initWithCoder:aDecoder];
    if (self) {
        
        // Compass icon
        UIImageView* compassView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 38, 38)];
        compassView.image = [UIImage imageNamed:@"mission_impossible"];
        self.compassView = compassView;
        [compassView release];
        
        [self addSubview:self.compassView];
        
        // Location icon
        UIImageView* locationView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 40, 40)];
        locationView.image = [UIImage imageNamed:@"mission_impossible"];
        self.locationView = locationView;
        [locationView release];
        
        [self addSubview:self.locationView];
        
        // Photo location icon
        UIImageView* photoLocationView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 200, 40, 40)];
        photoLocationView.image = [UIImage imageNamed:@"mission_impossible"];
        self.photoLocationView = photoLocationView;
        [photoLocationView release];
        
        [self addSubview:self.photoLocationView];
    }
    
    return self;
}

- (void)dealloc {
    
    self.building = nil;
    self.vertexArray = nil;
    self.delegate = nil;
    
    self.compassView = nil;
    self.locationView = nil;
    self.photoLocationView = nil;
    
    [super dealloc];
}

- (void)setBuilding:(Building *)building {
    
    if (_building != building) {
        [_building release];
        _building = [building retain];
    }
 
    if (building != nil) {
        [self prepare];
    }
}

- (void)setLocationEnabled:(BOOL)locationEnabled {
    
    // Turn on monitoring
    if (!_locationEnabled && locationEnabled) {
        if (self.locationManager == nil) {
            CLLocationManager* locationMgr = [[CLLocationManager alloc] init];
            self.locationManager = locationMgr;
            [locationMgr release];
            
            self.locationManager.delegate = self;
            self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
            self.locationManager.distanceFilter = 5.0f;
            self.locationManager.headingFilter = 1.0f;
        }
        
        self.locationView.hidden = NO;
        
        [self.locationManager startUpdatingLocation];
        [self.locationManager startUpdatingHeading];
    }
    // Turn off monitoring
    else if (_locationEnabled && !locationEnabled) {
        if (self.locationManager != nil) {
            [self.locationManager stopUpdatingLocation];
            [self.locationManager stopUpdatingHeading];
        }
        
        self.locationView.hidden = YES;
    }
    
    _locationEnabled = locationEnabled;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
    
    // Building shape
    [self drawBuilding];
    
    // Compass
    [self drawCompass];
    
    // Location and heading
    [self drawLocation];
    
    // Photo location and heading
    [self drawPhotoLocation];
}

#pragma mark - [CLLocationManagerDelegate]

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    
    self.coordinate = newLocation.coordinate;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
    
    self.heading = newHeading.trueHeading;
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    
    NSLog(@"Error occured");
}

#pragma mark - [Render]

- (void)drawBuilding {
    
    UIFont* font = [UIFont systemFontOfSize:8.0f];
 
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextBeginPath(context);
    CGContextSetLineWidth(context, 1.0f);
    CGContextSetFillColorWithColor(context, [UIColor colorWithRed:242.0 / 255 green:242.0 / 255 blue:242.0 / 255 alpha:1].CGColor);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    NSValue* value = [self.vertexArray objectAtIndex:0];
    CGPoint pt = value.CGPointValue;
    CGContextMoveToPoint(context, pt.x, pt.y);

    for (int i = 1; i < self.vertexArray.count; i++) {
        NSValue* v = [self.vertexArray objectAtIndex:i];
        CGPoint p = v.CGPointValue;
        CGContextAddLineToPoint(context, p.x, p.y);
    }
    
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);
    
    // Draw the selected side
    if (self.selectedSide != -1) {
        
        CGContextBeginPath(context);
        CGContextSetLineWidth(context, 3.0);
        CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
        
        NSValue* startValue = [self.vertexArray objectAtIndex:self.selectedSide];
        CGPoint startPoint = startValue.CGPointValue;
        CGContextMoveToPoint(context, startPoint.x, startPoint.y);
        
        CGPoint endPoint;
        if (self.selectedSide == self.vertexArray.count - 1) {
            NSValue* endValue = [self.vertexArray objectAtIndex:0];
            endPoint = endValue.CGPointValue;
        }
        else {
            NSValue* endValue = [self.vertexArray objectAtIndex:(self.selectedSide + 1)];
            endPoint = endValue.CGPointValue;
        }
        CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
        
        CGContextClosePath(context);
        CGContextDrawPath(context, kCGPathStroke);
    }
    
    // Draw the vertex index text
    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
    for (int i = 0; i < self.vertexArray.count; i++) {
        NSValue* v = [self.vertexArray objectAtIndex:i];
        CGPoint p = v.CGPointValue;
        CGRect vertexRect = CGRectMake(p.x - VERTEX_RADIUS,
                                       p.y - VERTEX_RADIUS,
                                       VERTEX_RADIUS * 2,
                                       VERTEX_RADIUS * 2);
        CGContextFillEllipseInRect(context, vertexRect);
        CGPoint fontPoint = CGPointMake(p.x + 1, p.y + 3);
        NSString* index = [NSString stringWithFormat:@"%d", i];
        [index drawAtPoint:fontPoint withFont:font];
    }
}

- (void)drawCompass {
    
    if (self.compassView != nil && self.compassView.hidden != NO) {
        CGFloat angle = 45 * M_PI / 180;//self.heading * M_PI / 180;
        self.compassView.transform = CGAffineTransformMakeRotation(angle);
    }
}

- (void)drawLocation {
    
    if (self.locationView != nil && !self.locationView.hidden) {
        CGPoint locationPoint = [self locationToScreenPoint:self.coordinate];
        
        // Out of screen, do not waste time drawing
        if (locationPoint.x < 0 || locationPoint.x > self.frame.size.width || locationPoint.y < 0 || locationPoint.y > self.frame.size.height) {
            return;
        }
        
        // Location
        CGRect frame = self.locationView.frame;
        frame.origin.x = locationPoint.x - frame.size.width / 2;
        frame.origin.y = locationPoint.y - frame.size.height / 2;
        self.locationView.frame = frame;
        
        // Angle
        self.locationView.transform = CGAffineTransformMakeRotation(self.heading * M_PI / 180);
    }
}

- (void)drawPhotoLocation {
    
    if (self.building != nil && self.selectedSide != -1 && self.building.photoInfoList.count > self.selectedSide) {
        
        PhotoInfo* photoInfo = [self.building.photoInfoList objectAtIndex:self.selectedSide];
        
        CGPoint photoLocationPoint = [self locationToScreenPoint:photoInfo.coordinate];
        
        // Out of screen, do not waste time drawing
        if (photoLocationPoint.x < 0 || photoLocationPoint.x > self.frame.size.width || photoLocationPoint.y < 0 ||photoLocationPoint.y > self.frame.size.height) {
            return;
        }
        
        // Position
        CGRect frame = self.photoLocationView.frame;
        frame.origin.x = photoLocationPoint.x - frame.size.width / 2;
        frame.origin.y = photoLocationPoint.y - frame.size.height / 2;
        self.photoLocationView.frame = frame;
        
        // Angle
        self.photoLocationView.transform = CGAffineTransformMakeRotation(photoInfo.direction * M_PI / 180);
    }
}

#pragma mark - [Private methods]

- (void)prepare {
    
    NSArray* indices = self.building.baseIndiceList;
    
    // 计算范围
    double minLog = 100000.0, maxLog = 0.0, minLat = 100000.0, maxLat = -100.0;
    
    for (NSValue* value in indices) {
        CGPoint point = value.CGPointValue;
        if (point.x < minLog) {
            minLog = point.x;
        }
        if (point.x > maxLog) {
            maxLog = point.x;
        }
        if (point.y < minLat) {
            minLat = point.y;
        }
        if (point.y > maxLat) {
            maxLat = point.y;
        }
    }
    
    double logGap = maxLog - minLog;
    double latGap = maxLat - minLat;
    
    BuildingBound bounds = self.buildingBound;
    bounds.minLog = minLog;
    bounds.maxLog = maxLog;
    bounds.minLat = minLat;
    bounds.maxLat = maxLat;
    self.buildingBound = bounds;
    
    CGRect frame = self.frame;
    
    CGRect rect = self.buildingRect;
    rect.size.height = frame.size.height * 3.0 / 4;
    rect.size.width = rect.size.height * logGap / latGap;
    rect.origin.x = (frame.size.width - rect.size.width) / 2;
    rect.origin.y = (frame.size.height - rect.size.height) / 2;
    self.buildingRect = rect;
    
    NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:1];
    for (int i = 0; i < self.building.baseIndiceList.count; i++) {
        NSValue* v = [self.building.baseIndiceList objectAtIndex:i];
        CGPoint coor = v.CGPointValue;
        CLLocationCoordinate2D location = {coor.y, coor.x};
        CGPoint point = [self locationToScreenPoint:location];
        
        NSValue* value = [NSValue valueWithCGPoint:point];
        [array addObject:value];
    }
    self.vertexArray = array;
    [array release];
    
    // 设置点击响应
    if (self.gestureRecognizers.count == 0) {
        
        UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapView:)];
        tapGestureRecognizer.numberOfTapsRequired = 1;
        [self addGestureRecognizer:tapGestureRecognizer];
        [tapGestureRecognizer release];
    }
}

- (void)onTapView:(UIGestureRecognizer *)gestureRecognizer {
    
    UIView* view = gestureRecognizer.view;
    if (view != self) {
        return;
    }
    
    CGPoint pt = [gestureRecognizer locationInView:view];
    const int expandRange = 5;
    CGRect effectiveRegrion = CGRectMake(self.buildingRect.origin.x - expandRange, 
                                         self.buildingRect.origin.y - expandRange, 
                                         self.buildingRect.size.width + expandRange * 2,
                                         self.buildingRect.size.height + expandRange * 2);
    if (!CGRectContainsPoint(effectiveRegrion, pt)) {
        NSLog(@"Not in effective region");
        return;
    }
    
    NSLog(@"x : %f, y : %f", pt.x, pt.y);
    
    // 判断是否点中了某条边
    int sideIndex = [self sideIndexOnTap:pt];
    if (sideIndex != -1) {
        if (sideIndex == self.selectedSide) {
            return;
        }
        
        self.selectedSide = sideIndex;
        // Rerender
        [self setNeedsDisplay];
        
        // Notify main view to update by
        if (self.delegate != nil) {
            [self.delegate onSelectSide:0];
        }
    }
}

- (int)sideIndexOnTap:(CGPoint)point {
    
    // 使用椭圆包围圈算法
    float min_d = 10000.0;
    int hitIndex = -1;
    
    for (int i = 0; i < self.vertexArray.count; i++) {
        NSValue* value = [self.vertexArray objectAtIndex:i];
        CGPoint p1 = value.CGPointValue;
        CGPoint p2;
        if (i == self.vertexArray.count - 1) {
            NSValue* value2 = [self.vertexArray objectAtIndex:0];
            p2 = value2.CGPointValue;
        }
        else {
            NSValue* value2 = [self.vertexArray objectAtIndex:(i + 1)];
            p2 = value2.CGPointValue;
        }
        
        float d1 = [self distanceOfPoint1:point point2:p1];
        float d2 = [self distanceOfPoint1:point point2:p2];
        float d = [self distanceOfPoint1:p1 point2:p2];
        
        float d_delta = d1 + d2 - d;
        if (d_delta < min_d) {
            min_d = d_delta;
            hitIndex = i;
        }
    }
    
    NSLog(@"min_d : %.1f, index : %d", min_d, hitIndex);
    if (hitIndex == -1) {
        return -1;
    }
    
    const float boundRadius = 4.0;
    if (min_d <= boundRadius) {
        NSLog(@"Hit");
        return hitIndex;
    }
    else {
        NSLog(@"Lost");
        return -1;
    }
}

- (CGFloat)distanceOfPoint1:(CGPoint)p1 point2:(CGPoint)p2 {
    
    return sqrtf((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}

- (CGPoint)locationToScreenPoint:(CLLocationCoordinate2D)coordinate {
    
    double ptInDegree = self.buildingRect.size.height / (self.buildingBound.maxLat - self.buildingBound.minLat);
    
    CGPoint point;
    point.x = (coordinate.longitude - self.buildingBound.minLog) * ptInDegree + self.buildingRect.origin.x;
    point.y = self.buildingRect.origin.y + (self.buildingBound.maxLat - coordinate.latitude) * ptInDegree;
    
    return point;
}

@end
